package com.smile.commons.gps.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.smile.commons.gps.PlanarAirlineGenerator;
import com.smile.commons.gps.PlanarAirlineService;
import com.smile.commons.gps.WpmzFileGenerator;
import com.smile.commons.gps.domain.CoordinatePointReq;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 面状航线生成示例
 * 演示如何使用面状航线生成工具
 * 
 * @author System
 * @since 2024-09-18
 */
public class PlanarAirlineExample {

    public static void main(String[] args) {
        System.out.println("=== 面状航线生成示例 ===");

//        // 示例1：矩形区域航线生成
//        demonstrateRectangularAirline();
//
//        System.out.println("\n" + "=".repeat(50) + "\n");
//
//        // 示例2：不规则多边形航线生成
//        demonstratePolygonAirline();
//
//        System.out.println("\n" + "=".repeat(50) + "\n");
//
//        // 示例3：航线优化演示
//        demonstrateAirlineOptimization();
//
//        System.out.println("\n" + "=".repeat(50) + "\n");
//
//        // 示例4：完整覆盖效果演示
//        demonstrateCompleteCoverage();
//
//        System.out.println("\n" + "=".repeat(50) + "\n");

        // 示例5：内凹多边形航线生成
        demonstrateConcavePolygonAirline();
        
        System.out.println("\n" + "=".repeat(50) + "\n");

        // 示例6：WPMZ文件生成演示
        demonstrateWpmzGeneration();
    }

    /**
     * 示例1：矩形区域航线生成
     */
    private static void demonstrateRectangularAirline() {
        System.out.println("示例1：矩形区域航线生成");
        System.out.println("区域：杭州西湖附近 1km x 1km 区域");

        // 1. 定义矩形边界点（杭州西湖附近）
        List<CoordinatePointReq> boundaryPoints = new ArrayList<>();
        boundaryPoints.add(createPoint(30.250000, 120.140000)); // 左下角
        boundaryPoints.add(createPoint(30.250000, 120.150000)); // 右下角
        boundaryPoints.add(createPoint(30.259000, 120.150000)); // 右上角
        boundaryPoints.add(createPoint(30.259000, 120.140000)); // 左上角

        // 2. 配置航线参数
        PlanarAirlineGenerator.AirlineConfig config = new PlanarAirlineGenerator.AirlineConfig();
        config.setFlightHeight(new BigDecimal("120"));    // 飞行高度120米
        config.setOverlapH(80);                           // 航向重叠率80%
        config.setOverlapW(60);                           // 旁向重叠率60%
        config.setDirection(0);                           // 航线方向（北向）
        config.setMargin(50);                             // 边界外扩50米

        // 3. 生成航线
        PlanarAirlineService.PlanarAirlineResult result = 
            PlanarAirlineService.generateCompletePlanarAirline(boundaryPoints, config, 12.0);

        // 4. 输出结果
        System.out.println("生成结果：");
        System.out.println(PlanarAirlineService.generateAirlineSummary(result));

        // 5. 输出前5个航点坐标
        printFirstFewWaypoints(result.getFlightPath(), 5);
    }

    /**
     * 示例2：不规则多边形航线生成
     */
    private static void demonstratePolygonAirline() {
        System.out.println("示例2：不规则多边形航线生成");
        System.out.println("区域：不规则五边形区域");

        // 1. 定义不规则五边形边界点
        List<CoordinatePointReq> boundaryPoints = new ArrayList<>();
        boundaryPoints.add(createPoint(30.250001, 120.140001));
        boundaryPoints.add(createPoint(30.252001, 120.145001));
        boundaryPoints.add(createPoint(30.256001, 120.147001));
        boundaryPoints.add(createPoint(30.258001, 120.143001));
        boundaryPoints.add(createPoint(30.254001, 120.138001));
        System.out.println(JSONObject.toJSONString(boundaryPoints));
        // 2. 配置高精度航线参数
        PlanarAirlineGenerator.AirlineConfig config = new PlanarAirlineGenerator.AirlineConfig();
        config.setFlightHeight(new BigDecimal("80"));     // 低空飞行80米
        config.setOverlapH(85);                           // 高航向重叠率85%
        config.setOverlapW(75);                           // 高旁向重叠率75%
        config.setDirection(45);                          // 45度航线方向
        config.setMargin(30);                             // 边界外扩30米

        // 3. 生成航线
        PlanarAirlineService.PlanarAirlineResult result = 
            PlanarAirlineService.generateCompletePlanarAirline(boundaryPoints, config, 8.0);

        // 4. 输出结果
        System.out.println("生成结果：");
        System.out.println(PlanarAirlineService.generateAirlineSummary(result));

        // 5. 输出前5个航点坐标
        printFirstFewWaypoints(result.getFlightPath(), 5);
    }

    /**
     * 示例3：航线优化演示
     */
    private static void demonstrateAirlineOptimization() {
        System.out.println("示例3：航线优化演示");

        // 1. 生成基础航线
        List<CoordinatePointReq> boundaryPoints = new ArrayList<>();
        boundaryPoints.add(createPoint(30.250000, 120.140000));
        boundaryPoints.add(createPoint(30.250000, 120.145000));
        boundaryPoints.add(createPoint(30.255000, 120.145000));
        boundaryPoints.add(createPoint(30.255000, 120.140000));

        PlanarAirlineGenerator.AirlineConfig config = new PlanarAirlineGenerator.AirlineConfig();
        List<CoordinatePointReq> originalPath = PlanarAirlineGenerator.generatePlanarAirline(boundaryPoints, config);

        System.out.println("原始航线航点数量: " + originalPath.size());

        // 2. 路径优化
        List<CoordinatePointReq> optimizedPath = 
            com.smile.commons.gps.AirlinePathOptimizer.optimizeFlightPath(originalPath, 0.00001);
        System.out.println("优化后航线航点数量: " + optimizedPath.size());

        // 3. 添加平滑转弯
        List<CoordinatePointReq> smoothPath = 
            com.smile.commons.gps.AirlinePathOptimizer.addTurnPoints(optimizedPath, 20.0);
        System.out.println("添加转弯点后航点数量: " + smoothPath.size());

        // 4. 计算统计信息
        double originalDistance = com.smile.commons.gps.AirlinePathOptimizer.calculateTotalDistance(originalPath);
        double optimizedDistance = com.smile.commons.gps.AirlinePathOptimizer.calculateTotalDistance(optimizedPath);
        double smoothDistance = com.smile.commons.gps.AirlinePathOptimizer.calculateTotalDistance(smoothPath);

        System.out.println(String.format("原始航线总长度: %.2f米", originalDistance));
        System.out.println(String.format("优化后航线总长度: %.2f米", optimizedDistance));
        System.out.println(String.format("平滑后航线总长度: %.2f米", smoothDistance));

        // 5. 坐标系转换示例
        System.out.println("\n坐标系转换示例：");
        CoordinatePointReq gaodePoint = createPoint(30.250000, 120.140000); // 模拟高德坐标
        System.out.println("原始坐标（高德）: " + formatCoordinate(gaodePoint));

        List<CoordinatePointReq> singlePoint = new ArrayList<>();
        singlePoint.add(gaodePoint);
        List<CoordinatePointReq> wgs84Points = PlanarAirlineService.convertCoordinateSystem(singlePoint, 2);
        System.out.println("转换后坐标（WGS84）: " + formatCoordinate(wgs84Points.get(0)));
    }

    /**
     * 创建坐标点
     */
    private static CoordinatePointReq createPoint(double lat, double lon) {
        CoordinatePointReq point = new CoordinatePointReq();
        point.setLatitude(new BigDecimal(lat));
        point.setLongitude(new BigDecimal(lon));
        point.setHeight(new BigDecimal("120"));
        return point;
    }

    /**
     * 打印前几个航点信息
     */
    private static void printFirstFewWaypoints(List<CoordinatePointReq> waypoints, int count) {
        System.out.println(JSONObject.toJSONString(waypoints));
        System.out.println("\n前" + Math.min(count, waypoints.size()) + "个航点坐标：");
        for (int i = 0; i < Math.min(count, waypoints.size()); i++) {
            CoordinatePointReq point = waypoints.get(i);
            System.out.println(String.format("航点%d: %s", i + 1, formatCoordinate(point)));
        }
    }

    /**
     * 示例4：严格边界限制效果演示
     */
    private static void demonstrateCompleteCoverage() {
        System.out.println("示例4：严格边界限制效果演示");
        System.out.println("区域：复杂不规则多边形区域（航点严格限制在边界内）");

        // 1. 定义一个复杂的L形区域
        List<CoordinatePointReq> boundaryPoints = new ArrayList<>();
        boundaryPoints.add(createPoint(30.250000, 120.140000)); // 起始点
        boundaryPoints.add(createPoint(30.250000, 120.146000)); // 右下
        boundaryPoints.add(createPoint(30.253000, 120.146000)); // 右下转角
        boundaryPoints.add(createPoint(30.253000, 120.148000)); // 右上
        boundaryPoints.add(createPoint(30.258000, 120.148000)); // 最右上
        boundaryPoints.add(createPoint(30.258000, 120.142000)); // 右上转角
        boundaryPoints.add(createPoint(30.255000, 120.142000)); // 中间点
        boundaryPoints.add(createPoint(30.255000, 120.140000)); // 左上

        // 2. 配置严格边界限制的航线参数
        PlanarAirlineGenerator.AirlineConfig config = new PlanarAirlineGenerator.AirlineConfig();
        config.setFlightHeight(new BigDecimal("100"));      // 低空飞行
        config.setOverlapH(80);                              // 高航向重叠率
        config.setOverlapW(70);                              // 高旁向重叠率
        config.setDirection(0);                              // 北向航线
        config.setMargin(0);                                 // 设置为0，不允许超出边界
        
        // 使用适中的相机参数
        config.setFocalLength(24.0);
        config.setSensorWidth(36.0);
        config.setSensorHeight(24.0);
        config.setImageWidth(4000);
        config.setImageHeight(3000);

        // 3. 生成航线
        System.out.println("正在生成严格边界限制的航线...");
        PlanarAirlineService.PlanarAirlineResult result = 
            PlanarAirlineService.generateCompletePlanarAirline(boundaryPoints, config, 12.0);

        // 4. 输出结果
        System.out.println("生成结果：");
        System.out.println(PlanarAirlineService.generateAirlineSummary(result));

        // 5. 输出详细的航点信息
        System.out.println("详细航点信息：");
        List<CoordinatePointReq> flightPath = result.getFlightPath();
        System.out.println(String.format("总共生成 %d 个航点", flightPath.size()));
        
        // 检查边界限制情况
        validateBoundaryCompliance(flightPath, boundaryPoints);
        
        // 输出前10个航点坐标
        printFirstFewWaypoints(result.getFlightPath(), 10);
        
        // 6. 验证起始点是否正确
        if (!flightPath.isEmpty()) {
            CoordinatePointReq firstWaypoint = flightPath.get(0);
            CoordinatePointReq startBoundary = boundaryPoints.get(0);
            double distance = calculateDistance(firstWaypoint, startBoundary);
            System.out.println(String.format("第一个航点距离边界起始点的距离：%.2f米", distance));
        }
    }
    
    /**
     * 验证边界合规性
     */
    private static void validateBoundaryCompliance(List<CoordinatePointReq> flightPath, List<CoordinatePointReq> boundaryPoints) {
        if (flightPath.isEmpty()) {
            System.out.println("警告：没有生成任何航点！");
            return;
        }
        
        int outOfBoundaryCount = 0;
        int totalPoints = flightPath.size();
        
        // 检查每个航点是否都在边界内
        for (CoordinatePointReq waypoint : flightPath) {
            if (!isPointInPolygonStrict(waypoint, boundaryPoints)) {
                outOfBoundaryCount++;
            }
        }
        
        System.out.println("边界合规性检查：");
        System.out.println(String.format("总航点数：%d", totalPoints));
        System.out.println(String.format("边界内航点：%d", totalPoints - outOfBoundaryCount));
        System.out.println(String.format("边界外航点：%d", outOfBoundaryCount));
        
        if (outOfBoundaryCount == 0) {
            System.out.println("✅ 验证通过：所有航点都严格限制在边界内");
        } else {
            double percentage = (double)outOfBoundaryCount / totalPoints * 100;
            System.out.println(String.format("❌ 验证失败：有 %d 个航点超出边界 (%.1f%%)", 
                outOfBoundaryCount, percentage));
        }
    }
    
    /**
     * 严格判断点是否在多边形内部
     */
    private static boolean isPointInPolygonStrict(CoordinatePointReq point, List<CoordinatePointReq> polygon) {
        double x = point.getLongitude().doubleValue();
        double y = point.getLatitude().doubleValue();
        
        boolean inside = false;
        int j = polygon.size() - 1;
        
        for (int i = 0; i < polygon.size(); i++) {
            double xi = polygon.get(i).getLongitude().doubleValue();
            double yi = polygon.get(i).getLatitude().doubleValue();
            double xj = polygon.get(j).getLongitude().doubleValue();
            double yj = polygon.get(j).getLatitude().doubleValue();
            
            if (((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) {
                inside = !inside;
            }
            j = i;
        }
        
        return inside;
    }
    
    /**
     * 分析航线覆盖情况
     */
    private static void analyzeCoverage(List<CoordinatePointReq> flightPath, List<CoordinatePointReq> boundaryPoints) {
        if (flightPath.isEmpty()) {
            System.out.println("警告：没有生成任何航点！");
            return;
        }
        
        // 计算边界框
        double minLat = boundaryPoints.stream().mapToDouble(p -> p.getLatitude().doubleValue()).min().orElse(0);
        double maxLat = boundaryPoints.stream().mapToDouble(p -> p.getLatitude().doubleValue()).max().orElse(0);
        double minLon = boundaryPoints.stream().mapToDouble(p -> p.getLongitude().doubleValue()).min().orElse(0);
        double maxLon = boundaryPoints.stream().mapToDouble(p -> p.getLongitude().doubleValue()).max().orElse(0);
        
        // 计算航点的边界框
        double waypointMinLat = flightPath.stream().mapToDouble(p -> p.getLatitude().doubleValue()).min().orElse(0);
        double waypointMaxLat = flightPath.stream().mapToDouble(p -> p.getLatitude().doubleValue()).max().orElse(0);
        double waypointMinLon = flightPath.stream().mapToDouble(p -> p.getLongitude().doubleValue()).min().orElse(0);
        double waypointMaxLon = flightPath.stream().mapToDouble(p -> p.getLongitude().doubleValue()).max().orElse(0);
        
        System.out.println("覆盖分析：");
        System.out.println(String.format("边界区域：纬度[%.6f, %.6f], 经度[%.6f, %.6f]", 
            minLat, maxLat, minLon, maxLon));
        System.out.println(String.format("航点覆盖：纬度[%.6f, %.6f], 经度[%.6f, %.6f]", 
            waypointMinLat, waypointMaxLat, waypointMinLon, waypointMaxLon));
        
        // 检查覆盖完整性
        boolean latCovered = waypointMinLat <= minLat && waypointMaxLat >= maxLat;
        boolean lonCovered = waypointMinLon <= minLon && waypointMaxLon >= maxLon;
        
        if (latCovered && lonCovered) {
            System.out.println("✓ 覆盖状态：完整覆盖");
        } else {
            System.out.println("⚠ 覆盖状态：可能存在覆盖不全");
            if (!latCovered) {
                System.out.println("纬度覆盖不完整");
            }
            if (!lonCovered) {
                System.out.println("经度覆盖不完整");
            }
        }
        
        // 计算航点密度
        double boundaryArea = calculatePolygonArea(boundaryPoints);
        double waypointDensity = flightPath.size() / (boundaryArea / 1000000.0); // 每平方公里航点数
        System.out.println(String.format("航点密度：%.1f 点/平方公里", waypointDensity));
    }
    
    /**
     * 计算多边形面积（使用Shoelace公式）
     */
    private static double calculatePolygonArea(List<CoordinatePointReq> polygon) {
        if (polygon.size() < 3) return 0;
        
        double area = 0;
        int n = polygon.size();
        
        for (int i = 0; i < n; i++) {
            int j = (i + 1) % n;
            double lat1 = Math.toRadians(polygon.get(i).getLatitude().doubleValue());
            double lon1 = Math.toRadians(polygon.get(i).getLongitude().doubleValue());
            double lat2 = Math.toRadians(polygon.get(j).getLatitude().doubleValue());
            double lon2 = Math.toRadians(polygon.get(j).getLongitude().doubleValue());
            
            area += (lon2 - lon1) * (2 + Math.sin(lat1) + Math.sin(lat2));
        }
        
        area = Math.abs(area) * 6378137.0 * 6378137.0 / 2.0; // 地球半径平方
        return area;
    }
    
    /**
     * 计算两点距离
     */
    private static double calculateDistance(CoordinatePointReq point1, CoordinatePointReq point2) {
        double lat1 = Math.toRadians(point1.getLatitude().doubleValue());
        double lon1 = Math.toRadians(point1.getLongitude().doubleValue());
        double lat2 = Math.toRadians(point2.getLatitude().doubleValue());
        double lon2 = Math.toRadians(point2.getLongitude().doubleValue());
        
        double deltaLat = lat2 - lat1;
        double deltaLon = lon2 - lon1;
        
        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
                   Math.cos(lat1) * Math.cos(lat2) *
                   Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        
        return 6378137.0 * c; // 地球半径 * 弧度
    }
    
    /**
     * 示例5：内凹多边形航线生成
     * 演示复杂内凹形状区域的航线生成，包括月牙形、星形等
     */
    private static void demonstrateConcavePolygonAirline() {
        System.out.println("示例5：内凹多边形航线生成");
        System.out.println("区域：复杂内凹形状区域（月牙形）");

        // 1. 生成优化的月牙形内凹多边形（提高精度和稳定性）
        List<CoordinatePointReq> boundaryPoints = generateOptimizedCrescentPolygon();
        
        System.out.println("优化后的月牙形边界点坐标：");
        System.out.println(JSONObject.toJSONString(boundaryPoints));

        // 2. 配置高精度航线参数（针对复杂形状优化）
        PlanarAirlineGenerator.AirlineConfig config = new PlanarAirlineGenerator.AirlineConfig();
        config.setFlightHeight(new BigDecimal("50"));       // 更低空飞行50米，提高精度
        config.setOverlapH(85);                              // 高航向重叠率85%
        config.setOverlapW(75);                              // 高旁向重叠率75%
        config.setDirection(0);                              // 0度航线方向（北向）
        config.setMargin(0);                                 // 不允许超出边界
        config.setGaodeOptimized(true);                      // 启用高德地图优化

        // 使用高精度相机参数
        config.setFocalLength(35.0);                         // 35mm焦距
        config.setSensorWidth(36.0);
        config.setSensorHeight(24.0);
        config.setImageWidth(8000);                          // 超高分辨率
        config.setImageHeight(6000);

        // 3. 生成航线
        System.out.println("正在生成内凹多边形航线（使用增强型边界检查）...");
        PlanarAirlineService.PlanarAirlineResult result = 
            PlanarAirlineService.generateCompletePlanarAirline(boundaryPoints, config, 25.0);

        // 4. 输出结果
        System.out.println("生成结果：");
        System.out.println(PlanarAirlineService.generateAirlineSummary(result));

        // 5. 分析内凹多边形的特殊情况
        analyzeConcavePolygonCoverage(result.getFlightPath(), boundaryPoints);
        
        // 6. 输出部分航点坐标
        printFirstFewWaypoints(result.getFlightPath(), 10);
        
        // 7. 验证边界合规性
        validateBoundaryCompliance(result.getFlightPath(), boundaryPoints);
        
        // 8. 输出其他内凹多边形示例
        demonstrateOtherConcaveShapes();
    }
    
    /**
     * 生成优化的月牙形多边形（解决边界超出问题）
     */
    private static List<CoordinatePointReq> generateOptimizedCrescentPolygon() {
        List<CoordinatePointReq> points = new ArrayList<>();
        double centerLat = 30.254000;
        double centerLon = 120.144000;
        
        // 优化参数：减小外半径，增大内外半径比例
        double outerRadius = 0.003;  // 减小外半径到约330米
        double innerRadius = 0.0015; // 增大内半径到约165米
        int points_count = 16;       // 增加点数到16个，提高平滑度
        
        // 使用更合理的角度分布，避免边界检查问题
        for (int i = 0; i < points_count; i++) {
            double angle = 2 * Math.PI * i / points_count;
            
            // 优化内外半径分配：使用正弦波进行平滑过渡
            double radiusRatio = 0.5 + 0.3 * Math.sin(4 * angle); // 创建更平滑的波浪效果
            double radius = innerRadius + (outerRadius - innerRadius) * radiusRatio;
            
            // 计算坐标时加入微小的随机偏移，避免数值精度问题
            double latOffset = radius * Math.cos(angle);
            double lonOffset = radius * Math.sin(angle);
            
            double lat = centerLat + latOffset;
            double lon = centerLon + lonOffset;
            
            points.add(createPoint(lat, lon));
        }
        
        // 验证多边形的有效性
        if (isValidPolygon(points)) {
            System.out.println("月牙形多边形生成成功，共 " + points.size() + " 个顶点");
        } else {
            System.out.println("警告：月牙形多边形可能存在问题");
        }
        
        return points;
    }
    
    /**
     * 验证多边形是否有效
     */
    private static boolean isValidPolygon(List<CoordinatePointReq> polygon) {
        if (polygon.size() < 3) {
            return false;
        }
        
        // 检查是否有重复点
        for (int i = 0; i < polygon.size(); i++) {
            for (int j = i + 1; j < polygon.size(); j++) {
                CoordinatePointReq p1 = polygon.get(i);
                CoordinatePointReq p2 = polygon.get(j);
                double distance = calculateDistance(p1, p2);
                if (distance < 1.0) { // 小于1米认为是重复点
                    return false;
                }
            }
        }
        
        // 检查是否有相交的边（简化检查）
        // 这里可以添加更复杂的相交检查，但为了简化暂时略过
        
        return true;
    }
    
    /**
     * 分析内凹多边形的覆盖情况
     */
    private static void analyzeConcavePolygonCoverage(List<CoordinatePointReq> flightPath, List<CoordinatePointReq> boundaryPoints) {
        System.out.println("\n内凹多边形覆盖分析：");
        
        if (flightPath.isEmpty()) {
            System.out.println("警告：没有生成任何航点！");
            return;
        }
        
        // 统计各区域的航点分布
        int centerPoints = 0;  // 中心区域航点
        int edgePoints = 0;    // 边缘区域航点
        int cornerPoints = 0;  // 凹陷区域航点
        
        double centerLat = boundaryPoints.stream().mapToDouble(p -> p.getLatitude().doubleValue()).average().orElse(0);
        double centerLon = boundaryPoints.stream().mapToDouble(p -> p.getLongitude().doubleValue()).average().orElse(0);
        
        for (CoordinatePointReq waypoint : flightPath) {
            double distance = calculateDistance(waypoint, createPoint(centerLat, centerLon));
            
            if (distance < 100) {
                centerPoints++;
            } else if (distance < 300) {
                edgePoints++;
            } else {
                cornerPoints++;
            }
        }
        
        System.out.println(String.format("中心区域航点：%d 个", centerPoints));
        System.out.println(String.format("边缘区域航点：%d 个", edgePoints));
        System.out.println(String.format("突出区域航点：%d 个", cornerPoints));
        
        // 计算覆盖密度
        double boundaryArea = calculatePolygonArea(boundaryPoints);
        double waypointDensity = flightPath.size() / (boundaryArea / 1000000.0);
        System.out.println(String.format("内凹多边形航点密度：%.1f 点/平方公里", waypointDensity));
        
        // 检查凹陷区域是否被有效覆盖
        boolean concaveAreasCovered = cornerPoints > 0;
        if (concaveAreasCovered) {
            System.out.println("✓ 凹陷区域覆盖：有效覆盖了内凹部分");
        } else {
            System.out.println("⚠ 凹陷区域覆盖：可能存在覆盖不全");
        }
    }
    
    /**
     * 演示其他内凹多边形示例
     */
    private static void demonstrateOtherConcaveShapes() {
        System.out.println("\n其他内凹多边形示例：");
        
        // 示例 A：L 形区域
        System.out.println("\n示例 A：L 形内凹区域");
        List<CoordinatePointReq> lShapePoints = createLShapePolygon();
        testConcaveShape(lShapePoints, "L形");
        
        // 示例 B：队形区域
        System.out.println("\n示例 B：队形内凹区域");
        List<CoordinatePointReq> uShapePoints = createUShapePolygon();
        testConcaveShape(uShapePoints, "队形");
        
        // 示例 C：复杂不规则内凹形
        System.out.println("\n示例 C：复杂不规则内凹形");
        List<CoordinatePointReq> complexPoints = createComplexConcavePolygon();
        testConcaveShape(complexPoints, "复杂内凹形");
    }
    
    /**
     * 创建 L 形多边形
     */
    private static List<CoordinatePointReq> createLShapePolygon() {
        List<CoordinatePointReq> points = new ArrayList<>();
        double baseLat = 30.250000;
        double baseLon = 120.140000;
        
        points.add(createPoint(baseLat, baseLon));                    // 左下角
        points.add(createPoint(baseLat, baseLon + 0.006));            // 中下
        points.add(createPoint(baseLat + 0.003, baseLon + 0.006));    // 中上（内凹点）
        points.add(createPoint(baseLat + 0.003, baseLon + 0.009));    // 右上
        points.add(createPoint(baseLat + 0.006, baseLon + 0.009));    // 右上角
        points.add(createPoint(baseLat + 0.006, baseLon));            // 左上角
        
        return points;
    }
    
    /**
     * 创建 U 形多边形
     */
    private static List<CoordinatePointReq> createUShapePolygon() {
        List<CoordinatePointReq> points = new ArrayList<>();
        double baseLat = 30.252000;
        double baseLon = 120.142000;
        
        points.add(createPoint(baseLat, baseLon));                    // 左下外
        points.add(createPoint(baseLat, baseLon + 0.008));            // 右下外
        points.add(createPoint(baseLat + 0.005, baseLon + 0.008));    // 右上外
        points.add(createPoint(baseLat + 0.005, baseLon + 0.006));    // 右上内
        points.add(createPoint(baseLat + 0.002, baseLon + 0.006));    // 右下内（内凹）
        points.add(createPoint(baseLat + 0.002, baseLon + 0.002));    // 左下内（内凹）
        points.add(createPoint(baseLat + 0.005, baseLon + 0.002));    // 左上内
        points.add(createPoint(baseLat + 0.005, baseLon));            // 左上外
        
        return points;
    }
    
    /**
     * 创建复杂不规则内凹多边形
     */
    private static List<CoordinatePointReq> createComplexConcavePolygon() {
        List<CoordinatePointReq> points = new ArrayList<>();
        double baseLat = 30.254000;
        double baseLon = 120.146000;
        
        // 创建一个更复杂的内凹形状，类似于手掌形状
        points.add(createPoint(baseLat, baseLon));                    // 手腕底部
        points.add(createPoint(baseLat + 0.002, baseLon + 0.001));    // 手腕右下
        points.add(createPoint(baseLat + 0.003, baseLon));            // 拇指底部
        points.add(createPoint(baseLat + 0.004, baseLon + 0.001));    // 拇指尖
        points.add(createPoint(baseLat + 0.0035, baseLon + 0.0015));  // 拇指右侧
        points.add(createPoint(baseLat + 0.005, baseLon + 0.002));    // 食指尖
        points.add(createPoint(baseLat + 0.0045, baseLon + 0.0025));  // 食指右侧
        points.add(createPoint(baseLat + 0.0055, baseLon + 0.003));   // 中指尖
        points.add(createPoint(baseLat + 0.005, baseLon + 0.0035));   // 中指右侧
        points.add(createPoint(baseLat + 0.0045, baseLon + 0.004));   // 无名指尖
        points.add(createPoint(baseLat + 0.004, baseLon + 0.0045));   // 无名指右侧
        points.add(createPoint(baseLat + 0.0035, baseLon + 0.005));   // 小指尖
        points.add(createPoint(baseLat + 0.003, baseLon + 0.0048));   // 小指左侧
        points.add(createPoint(baseLat + 0.002, baseLon + 0.0045));   // 手掌右上
        points.add(createPoint(baseLat + 0.0015, baseLon + 0.003));   // 手掌中部（内凹）
        points.add(createPoint(baseLat + 0.001, baseLon + 0.004));    // 手掌左上
        points.add(createPoint(baseLat + 0.0005, baseLon + 0.002));   // 手腕左上
        
        return points;
    }
    
    /**
     * 测试内凹形状的航线生成
     */
    private static void testConcaveShape(List<CoordinatePointReq> boundaryPoints, String shapeName) {
        // 配置基本参数
        PlanarAirlineGenerator.AirlineConfig config = new PlanarAirlineGenerator.AirlineConfig();
        config.setFlightHeight(new BigDecimal("80"));
        config.setOverlapH(85);
        config.setOverlapW(75);
        config.setDirection(0);
        config.setMargin(0);
        config.setGaodeOptimized(true);
        
        // 生成航线
        try {
            PlanarAirlineService.PlanarAirlineResult result = 
                PlanarAirlineService.generateCompletePlanarAirline(boundaryPoints, config, 15.0);
            
            System.out.println(String.format("%s形状航线生成结果：", shapeName));
            System.out.println(String.format("航点数量：%d 个", result.getFlightPath().size()));
            
            if (!result.getFlightPath().isEmpty()) {
                // 验证边界合规性
                validateBoundaryCompliance(result.getFlightPath(), boundaryPoints);
                
                // 计算航线总长度
                double totalDistance = calculateTotalFlightDistance(result.getFlightPath());
                System.out.println(String.format("航线总长度：%.2f 米", totalDistance));
            } else {
                System.out.println("警告：未生成任何航点");
            }
        } catch (Exception e) {
            System.out.println(String.format("%s形状航线生成失败：%s", shapeName, e.getMessage()));
        }
    }
    
    /**
     * 计算航线总长度
     */
    private static double calculateTotalFlightDistance(List<CoordinatePointReq> flightPath) {
        if (flightPath.size() < 2) return 0;
        
        double totalDistance = 0;
        for (int i = 1; i < flightPath.size(); i++) {
            totalDistance += calculateDistance(flightPath.get(i-1), flightPath.get(i));
        }
        return totalDistance;
    }
    
    /**
     * 格式化坐标输出
     */
    private static String formatCoordinate(CoordinatePointReq point) {
        return String.format("纬度=%.6f, 经度=%.6f, 高度=%.1f米", 
            point.getLatitude().doubleValue(), 
            point.getLongitude().doubleValue(), 
            point.getHeight().doubleValue());
    }
    
    /**
     * 示例6：WPMZ文件生成演示
     */
    private static void demonstrateWpmzGeneration() {
        System.out.println("示例6：WPMZ文件生成演示");
        System.out.println("基于面状航线数据生成WPMZ文件");
        
        try {
            // 1. 定义示例区域（复杂L形）
            List<CoordinatePointReq> boundaryPoints = new ArrayList<>();
            boundaryPoints.add(createPoint(30.250000, 120.140000)); // 起始点
            boundaryPoints.add(createPoint(30.250000, 120.146000)); // 右下
            boundaryPoints.add(createPoint(30.253000, 120.146000)); // 右下转角
            boundaryPoints.add(createPoint(30.253000, 120.148000)); // 右上
            boundaryPoints.add(createPoint(30.258000, 120.148000)); // 最右上
            boundaryPoints.add(createPoint(30.258000, 120.142000)); // 右上转角
            boundaryPoints.add(createPoint(30.255000, 120.142000)); // 中间点
            boundaryPoints.add(createPoint(30.255000, 120.140000)); // 左上
            System.out.println(JSON.toJSONString(boundaryPoints));
            // 2. 配置航线参数
            PlanarAirlineGenerator.AirlineConfig config = new PlanarAirlineGenerator.AirlineConfig();
            config.setFlightHeight(new BigDecimal("100"));
            config.setOverlapH(80);
            config.setOverlapW(70);
            config.setDirection(0);
            config.setMargin(20);
            
            // 3. 生成面状航线
            PlanarAirlineService.PlanarAirlineResult airlineResult = 
                PlanarAirlineService.generateCompletePlanarAirline(boundaryPoints, config, 15.0);
            
            System.out.println("生成的航线数据（前5个点）：");
            printFirstFewWaypoints(airlineResult.getFlightPath(), 5);
            
            // 4. 生成WPMZ文件（默认配置，使用新的边界修复方法）
            System.out.println("\n生成WPMZ文件（使用原始边界数据）...");
            WpmzFileGenerator.WpmzGenerationResult wpmzResult1 = 
                WpmzFileGenerator.generateWpmzFromAirlineResultWithBoundary(
                    airlineResult, boundaryPoints, "example_l_shaped_mission_fixed");
            
            System.out.println("修复后WPMZ结果（使用原始边界）：");
            System.out.println("文件路径: " + wpmzResult1.getWpmzFilePath());
            System.out.println("摘要: " + wpmzResult1.getSummary());
            
            // 5. 生成WPMZ文件（旧方法，对比使用）
            System.out.println("\n生成WPMZ文件（旧方法，可能边框不正确）...");
            WpmzFileGenerator.WpmzGenerationResult wpmzResultOld = 
                WpmzFileGenerator.generateWpmzFromFlightPath(
                    airlineResult.getFlightPath(), "example_l_shaped_mission_old");
            
            System.out.println("旧方法WPMZ结果：");
            System.out.println("文件路径: " + wpmzResultOld.getWpmzFilePath());
            System.out.println("摘要: " + wpmzResultOld.getSummary());
            
            // 6. 生成WPMZ文件（自定义配置 + 原始边界）
            System.out.println("\n生成WPMZ文件（自定义配置 + 原始边界）...");
            WpmzFileGenerator.WpmzConfig wpmzConfig = new WpmzFileGenerator.WpmzConfig();
            wpmzConfig.setFileName("example_custom_l_shaped_mission_fixed");
            wpmzConfig.setFlightHeight(new BigDecimal("80"));  // 低空飞行
            wpmzConfig.setFlightSpeed(6.0);                    // 较快速度
            wpmzConfig.setDroneEnumValue(89);                  // 特定无人机型号
            wpmzConfig.setTemplateType("mapping2d");           // 建图航拍模式
            wpmzConfig.setAuthor("PlanarAirlineExample");

            WpmzFileGenerator.WpmzGenerationResult wpmzResult2 =
                WpmzFileGenerator.generateWpmzFromAirlineResultWithBoundary(
                    airlineResult, boundaryPoints, wpmzConfig);

            System.out.println("自定义配置WPMZ结果（使用原始边界）：");
            System.out.println("文件路径: " + wpmzResult2.getWpmzFilePath());
            System.out.println("摘要: " + wpmzResult2.getSummary());
            // 7. 直接从航线结果生成（旧方法，保留兼容性）
            System.out.println("\n直接从航线结果生成WPMZ文件（旧方法）...");
            WpmzFileGenerator.WpmzGenerationResult wpmzResult3 = 
                WpmzFileGenerator.generateWpmzFromAirlineResult(
                    airlineResult, "example_direct_from_result");
            
            System.out.println("直接生成WPMZ结果（旧方法）：");
            System.out.println("文件路径: " + wpmzResult3.getWpmzFilePath());
            System.out.println("摘要: " + wpmzResult3.getSummary());
            
            // 8. 输出总结
            System.out.println("\n=== WPMZ文件生成演示总结 ===");
            System.out.println("成功生成 4 个WPMZ文件：");
            System.out.println("1. 修复后默认配置（使用原始边界）: " + wpmzResult1.getWaypointCount() + " 个航点");
            System.out.println("2. 旧方法默认配置（可能边框不正确）: " + wpmzResultOld.getWaypointCount() + " 个航点");
            System.out.println("3. 修复后自定义配置（使用原始边界）: " + wpmzResult2.getWaypointCount() + " 个航点");
            System.out.println("4. 旧方法直接生成: " + wpmzResult3.getWaypointCount() + " 个航点");
            System.out.println();
            System.out.println("✅ 关键修复说明：");
            System.out.println("   - 新增 generateWpmzFromAirlineResultWithBoundary 方法");
            System.out.println("   - 解决了生成的航线文件边框不正确的问题");
            System.out.println("   - 使用实际的面状航线边界数据生成正确的边框");
            
        } catch (Exception e) {
            System.err.println("WPMZ文件生成演示失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
}